package net.sudot.chess.business.service;

import net.sudot.chess.exception.BusinessException;
import net.sudot.chess.business.controller.websocket.ChatWebSocketServer;
import net.sudot.chess.business.dao.RoomDao;
import net.sudot.chess.business.model.Customer;
import net.sudot.chess.business.model.Game;
import net.sudot.chess.business.model.OrderGame;
import net.sudot.chess.business.model.Room;
import net.sudot.chess.business.model.RoomCustomerRef;
import net.sudot.chess.business.model.RoomServiceRecord;
import net.sudot.chess.constant.LockConstants;
import net.sudot.chess.event.game.ClosedEvent;
import net.sudot.chess.event.game.ExitedEvent;
import net.sudot.chess.event.game.JoinedEvent;
import net.sudot.chess.event.game.StartedEvent;
import net.sudot.commons.lock.Lock;
import net.sudot.commons.pagination.Pageable;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Optional;

/**
 * 房间
 *
 * @author tangjialin on 2018-03-03.
 */
@Service
public class RoomService extends BaseService<Room> {
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;
    @Resource
    private RoomDao roomDao;
    @Resource
    private OrderGameService orderGameService;
    @Resource
    private GameService gameService;
    @Resource
    private RoomCustomerRefService roomCustomerRefService;
    @Resource
    private CustomerService customerService;
    @Resource
    private RoomServiceRecordService roomServiceRecordService;

    /**
     * 获取等待中的房间信息
     *
     * @param gameId 游戏主键
     * @return 返回房间信息
     */
    public Room findWaitByGame(Long gameId) {
        return roomDao.findWaitByGame(gameId);
    }

    /**
     * 获取游戏中的房间信息
     *
     * @param wechatGroupId 微信群主键
     * @return 返回房间信息
     */
    public Room findPlayingByWechatGroup(Long wechatGroupId) {
        return roomDao.findPlayingByWechatGroup(wechatGroupId);
    }

    /**
     * 获取所有游戏中的房间
     *
     * @return 返回游戏中的房间
     */
    public List<Room> findAllListAtPlaying() {
        return roomDao.findList("status", Room.Status.PLAYING);
    }

    /**
     * 获取客户加入且未关闭的房间
     *
     * @param customerId 客户主键
     * @return 返回客户正在参与的房间
     */
    public List<Room> findListOpenByCustomer(Long customerId) {
        return roomDao.findListOpenByCustomer(customerId);
    }

    /**
     * 获取已完成的房间信息
     *
     * @param pageable   分页参数
     * @param customerId 客户主键
     * @return 返回已完成的房间信息
     */
    public List<Room> findListCompleteByCustomer(Pageable pageable, Long customerId) {
        return roomDao.findListCompleteByCustomer(pageable, customerId);
    }

    /**
     * 加入房间
     *
     * @param room     加入的房间
     * @param customer 加入的玩家
     * @throws BusinessException 加入失败
     */
    @Lock(name = LockConstants.PLAY_GAME, value = "#room.gameId")
    @Transactional(rollbackFor = Exception.class)
    public Room join(Room room, Customer customer) throws BusinessException {
        Long gameId = room.getGameId();
        Game game = gameService.find(gameId);
        if (game == null) { throw new BusinessException("游戏不存在或已下架"); }
        BigDecimal customerBalance = Optional.ofNullable(customer.getBalance()).orElse(BigDecimal.ZERO);
        BigDecimal price = Optional.ofNullable(game.getPrice()).orElse(BigDecimal.ZERO);
        if (customerBalance.compareTo(price) < 0) {
            throw new BusinessException("金豆不足,请充值");
        }

        List<Room> roomList = findListOpenByCustomer(customer.getId());
        if (!roomList.isEmpty()) {
            throw new BusinessException("您已报名游戏,不能报名此游戏");
        }
        Long roomId = room.getId();
        room = roomId != null ? find(roomId) : findWaitByGame(gameId);
        if (room == null) {
            if (roomId != null) { throw new BusinessException("游戏已开始或房间不存在,请返回游戏大厅重新进入"); }
            room = new Room();
            room.setGameId(gameId);
            room.setName(game.getName());
            room.setPeriod(game.getPeriod());
            room.setPrice(price);
            room.setRegion(game.getRegion());
            room.setMaxPeople(game.getPeople());
            room.setPeople(0);
            room.setStatus(Room.Status.WAIT);
        } else if (room.getStatus() == Room.Status.PLAYING) {
            throw new BusinessException("游戏已开始,请返回游戏大厅重新进入");
        } else if (room.getStatus() == Room.Status.END) {
            throw new BusinessException("游戏已结束,请返回游戏大厅重新进入");
        }
        Integer people = room.getPeople();
        if (people >= room.getMaxPeople()) { throw new BusinessException("房间人数已满,请返回游戏大厅重新进入"); }
        room.setPeople(people + 1);
        if (room.getPeople().equals(room.getMaxPeople())) {
            // 参与人数已满(修改房间状态,房号在监听StartedEvent事件处理器中处理
            // room.setSn("房号正在分配中,若长时间未发放房号,请联系客服");
            room.setStatus(Room.Status.PLAYING);
        }
        room = saveOrUpdate(room);

        // 游戏房间和客户关联
        RoomCustomerRef roomCustomerRef = new RoomCustomerRef();
        roomCustomerRef.setCustomerId(customer.getId());
        roomCustomerRef.setRoomId(room.getId());
        roomCustomerRef.setClosed(false);
        roomCustomerRefService.save(roomCustomerRef);

        // 创建订单
        orderGameService.create(room, customer);

        applicationEventPublisher.publishEvent(new JoinedEvent(this, room, customer));

        // 房间人数未满,未开启游戏
        if (room.getStatus() != Room.Status.PLAYING) { return room; }
        /* 游戏已开启 */
        // 游戏期数加一
        game.setPeriod(game.getPeriod() + 1);
        gameService.update(game);

        // 添加客服
        addCustomerService(room);

        applicationEventPublisher.publishEvent(new StartedEvent(this, room));
        return room;
    }

    /**
     * 添加客服
     *
     * @param room 需要添加客服的room
     */
    @Lock(name = LockConstants.PLAY_GAME, value = "#room.id")
    @Transactional(rollbackFor = Exception.class)
    public void addCustomerService(Room room) {
        Customer customer = customerService.findGameMasterByRoom(room.getId());
        // 已有管理员,不再添加
        if (customer != null) { return; }
        Instant start = Instant.now().truncatedTo(ChronoUnit.DAYS);
        Instant end = start.plus(1, ChronoUnit.DAYS);
        customer = customerService.findMinCountByDate(start, end);
        // 当前没有管理员
        if (customer == null) { return; }
        // 游戏房间和游戏管理员关联
        RoomCustomerRef roomCustomerRef = new RoomCustomerRef();
        roomCustomerRef.setCustomerId(customer.getId());
        roomCustomerRef.setRoomId(room.getId());
        roomCustomerRef.setClosed(true);
        roomCustomerRefService.save(roomCustomerRef);

        // 添加游戏管理员对房间的服务记录
        RoomServiceRecord roomServiceRecord = new RoomServiceRecord();
        roomServiceRecord.setRoomId(room.getId());
        roomServiceRecord.setGameMaster(customer.getId());
        roomServiceRecordService.save(roomServiceRecord);

        // 发送游戏玩家加入信息
        ChatWebSocketServer.sendMessageAtPlayerAdd(room.getId());
    }

    /**
     * 退出房间
     *
     * @param room     退出的房间
     * @param customer 退出的玩家
     * @throws BusinessException 退出失败
     */
    @Lock(name = LockConstants.PLAY_GAME, value = "#room.gameId")
    @Transactional(rollbackFor = Exception.class)
    public void exit(Room room, Customer customer) throws BusinessException {
        RoomCustomerRef roomCustomerRef = roomCustomerRefService.findByRoomAndCustomer(room.getId(), customer.getId());
        if (roomCustomerRef == null) { throw new BusinessException("您未加入此游戏,不能退出"); }
        if (room.getStatus() == Room.Status.PLAYING) {
            throw new BusinessException("游戏已开始,不能退出游戏");
        }
        if (room.getStatus() == Room.Status.END) {
            throw new BusinessException("游戏已结束,不能退出游戏");
        }
        room.setPeople(room.getPeople() - 1);
        update(room);
        roomCustomerRefService.delete(roomCustomerRef.getId());
        OrderGame order = orderGameService.findUnComplete(room.getId(), customer.getId());
        if (order != null) {
            orderGameService.cancel(order);
        }

        applicationEventPublisher.publishEvent(new ExitedEvent(this, room, customer));
    }

    /**
     * 关闭房间
     *
     * @param room 关闭的房间
     * @throws BusinessException 关闭失败
     */
    @Transactional(rollbackFor = Exception.class)
    public void complete(Room room) throws BusinessException {
        if (room.getStatus() != Room.Status.PLAYING) {
            return;
        }
        room.setCloseDate(Instant.now());
        room.setStatus(Room.Status.END);
        update(room);
        List<OrderGame> orderList = orderGameService.findListUnComplete(room.getId());
        for (OrderGame order : orderList) {
            orderGameService.complete(order);
        }
        applicationEventPublisher.publishEvent(new ClosedEvent(this, room));
    }

    /**
     * 解散房间
     *
     * @param room 解散的房间
     * @throws BusinessException 解散失败
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancel(Room room) throws BusinessException {
        if (room.getStatus() != Room.Status.WAIT && room.getStatus() != Room.Status.PLAYING) {
            return;
        }
        room.setCloseDate(Instant.now());
        room.setStatus(Room.Status.CANCEL);
        update(room);
        List<OrderGame> orderList = orderGameService.findListUnComplete(room.getId());
        for (OrderGame order : orderList) {
            orderGameService.cancel(order);
        }
        applicationEventPublisher.publishEvent(new ClosedEvent(this, room));
    }

    /**
     * 申请关闭房间
     *
     * @param room     关闭的房间
     * @param customer 发起关闭房间的客户
     */
    @Transactional(rollbackFor = Exception.class)
    public void applyComplete(Room room, Customer customer) {
        List<RoomCustomerRef> customerRefList = roomCustomerRefService.findByRoom(room.getId());
        if (customer.isGameMaster()) {
            for (RoomCustomerRef customerRef : customerRefList) {
                if (!Boolean.TRUE.equals(customerRef.getClosed())) {
                    customerRef.setClosed(true);
                }
            }
            complete(room);
        } else {
            int count = 0;
            Long customerId = customer.getId();
            for (RoomCustomerRef customerRef : customerRefList) {
                if (customerRef.getCustomerId().equals(customerId) && !Boolean.TRUE.equals(customerRef.getClosed())) {
                    customerRef.setClosed(true);
                }
                if (Boolean.TRUE.equals(customerRef.getClosed())) {
                    count++;
                }
            }
            if (count == customerRefList.size()) {
                // 若已发起房间关闭的人数等与总人数,则关闭房间
                complete(room);
            }
        }
    }

}
